package com.huqi.beanutils;

import com.huqi.json.JsonUtil;
import com.huqi.log.LoggerUtil;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.sql.Timestamp;
import java.util.*;

/**
 * 类型转换工具类
 * @author 胡琦
 * @date 2021-07-24 星期六 15:47
 */
public class ModelUtil {
    private final static Logger LOGGER = LoggerFactory.getLogger(ModelUtil.class);

    public static <T> T dtoToModel(Object dto, Class<T> modelClazz) {
        T t = null;
        if (dto == null) {
            return null;
        }
        try {
            dto = deepCopyBean(dto);
            t = modelClazz.newInstance();
            BeanUtils.copyProperties(dto, t);
        }catch (Exception e) {
            LoggerUtil.error(e, LOGGER, "ModelUtil dtoToModel 对象复制异常 Object = {0}", JsonUtil.toJson(dto));
            return null;
        }
        return t;
    }

    /**
     *  深拷贝,序列化不采用
     * @param source
     * @param <T>
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> T deepCopyBean(T source) {
        if (source == null) {
            return null;
        }
        try {
            if (source instanceof Collection) {
                return (T) deepCopyCollection((Collection) source);
            }
            if (source.getClass().isArray()) {
                return (T) deepCopyArray(source);
            }
            if (source instanceof Map) {
                return (T) deepCopyMap((Map) source);
            }
            if (source instanceof Date) {
                return (T) new Date(((Date) source).getTime());
            }
            if (source instanceof Timestamp) {
                return (T) new Timestamp(((Timestamp) source).getTime());
            }
            // 基本类型直接返回原值
            if (source.getClass().isPrimitive() || source instanceof String || source instanceof Boolean
                    || Number.class.isAssignableFrom(source.getClass())) {
                return source;
            }
            if (source instanceof StringBuilder) {
                return (T) new StringBuilder(source.toString());
            }
            if (source instanceof StringBuffer) {
                return (T) new StringBuffer(source.toString());
            }
            Object dest = source.getClass().newInstance();
            BeanUtilsBean bean = BeanUtilsBean.getInstance();
            PropertyDescriptor[] origDescriptors = bean.getPropertyUtils().getPropertyDescriptors(source);
            for (int i = 0; i < origDescriptors.length; i++) {
                String name = origDescriptors[i].getName();
                if ("class".equals(name)) {
                    continue;
                }

                if (bean.getPropertyUtils().isReadable(source, name)
                        && bean.getPropertyUtils().isWriteable(dest, name)) {
                    try {
                        Object value = deepCopyBean(bean.getPropertyUtils().getSimpleProperty(source, name));
                        bean.getPropertyUtils().setSimpleProperty(dest, name, value);
                    } catch (NoSuchMethodException e) {
                        LOGGER.error("deepCopyBean", e);
                    }
                }
            }
            return (T) dest;
        } catch (Exception e) {
            LOGGER.error("deepCopyBean", e);
            return null;
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private static Collection deepCopyCollection(Collection source)
            throws InstantiationException, IllegalAccessException {
        Collection dest = source.getClass().newInstance();
        for (Object o : source) {
            dest.add(deepCopyBean(o));
        }
        return dest;
    }

    private static Object deepCopyArray(Object source) throws InstantiationException, IllegalAccessException,
            ArrayIndexOutOfBoundsException, IllegalArgumentException {
        int length = Array.getLength(source);
        Object dest = Array.newInstance(source.getClass().getComponentType(), length);
        if (length == 0) {
            return dest;
        }
        for (int i = 0; i < length; i++) {
            Array.set(dest, i, deepCopyBean(Array.get(source, i)));
        }
        return dest;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private static Map deepCopyMap(Map source) throws InstantiationException, IllegalAccessException {
        Map dest = source.getClass().newInstance();
        for (Object o : source.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            dest.put(deepCopyBean(e.getKey()), deepCopyBean(e.getValue()));
        }
        return dest;
    }
}

